/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.googlecode.mindlistening.tray;

import com.googlecode.mindlistening.events.ExitApplicationMessage;
import com.googlecode.mindlistening.util.CouchLocator;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bushe.swing.event.EventBus;
import org.bushe.swing.event.EventSubscriber;
import org.ektorp.CouchDbConnector;
import org.ektorp.CouchDbInstance;
import org.ektorp.http.HttpClient;

/**
 *  The job for this class is to make sure couchdb is alive and well on this machine.
 * 
 * @author ryan
 */
public class CouchDBProcessManager implements EventSubscriber<ExitApplicationMessage> {

    private CouchLocator locator;
    private String couchExe;
    private CouchDbConnector wikiConnector;
    private CouchDbInstance dbInstance;
    private HttpClient couchHttpClient;
    private boolean running = true;
    protected Process couchProcess;
    private InputStream couchStream;
    private boolean couchIsUp = false;

    public CouchDBProcessManager(CouchLocator locator,CouchDbConnector wikiConnector, CouchDbInstance dbInstance, HttpClient couchHttpClient )  {
        this.locator = locator;
        this.wikiConnector = wikiConnector;
        this.dbInstance = dbInstance;
        this.couchHttpClient = couchHttpClient;
        EventBus.subscribeStrongly(ExitApplicationMessage.class, this);
    }


    @Override
    public void onEvent(ExitApplicationMessage t) {
        running = false;
        Logger.getLogger(CouchDBProcessManager.class.getName()).log(Level.INFO, "Killing couch.");
        if (couchProcess != null) {
            couchProcess.destroy();
        }
    }

    protected boolean isCouchRunning() {
        try {
            List<String> names = dbInstance.getAllDatabases();
            if (names != null && names.size() > 0) return true;
        } catch (Exception e) {};
        return false;
    }

    /**
     * This method is synchronous. It only returns if couchdb is running.
     *
     */
    public void ensureCouchDB() {
        if (isCouchRunning()) return;
        couchExe = locator.findCouchDB();
        if (couchExe != null) {
            Logger.getLogger(CouchDBProcessManager.class.getName()).log(Level.INFO, "CouchDB exe found at: " + couchExe);
            new Thread(new CouchSupervisor()).start();
            for (int i =0; i < 4; i++) {
                if (couchIsUp) return;
                try {
                    Thread.sleep(2100);
                } catch (InterruptedException ex) {
                    Logger.getLogger(CouchDBProcessManager.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
        throw new RuntimeException("Couch DB could not be located or started. This is critical to the application");
    }


    private class CouchSupervisor implements Runnable {
        @Override
        public void run() {
            while(running) {
                if (!isCouchRunning()) {
                    couchIsUp = false;
                    Logger.getLogger(CouchDBProcessManager.class.getName()).log(Level.INFO, "CouchDB is not found to be running.");
                    startLocalCouch();
                } else {
                    couchIsUp = true;
                }
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException ex) {
                    Logger.getLogger(CouchDBProcessManager.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }


    private void startLocalCouch() {
        new Thread(new CouchRunner()).start();
    }

    private class CouchRunner implements Runnable {
        @Override
        public void run() {
             try {
                Logger.getLogger(CouchDBProcessManager.class.getName()).log(Level.INFO, "Starting couch.");


                ProcessBuilder pb = new ProcessBuilder(new String[]{couchExe});
                pb.redirectErrorStream(true);
                couchProcess = pb.start();
                couchStream = couchProcess.getInputStream();
                int chr = couchStream.read();
                while (chr != -1 && running) {
                    chr = couchStream.read();
                }
            } catch (IOException ex) {
                Logger.getLogger(CouchDBProcessManager.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}
